package com.corticon.dialogs.dao.hibernate;

import static org.hibernate.criterion.Restrictions.*;

import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.MatchMode;

import org.springframework.stereotype.Repository;

import com.corticon.dialogs.dao.QuestionAvailabilityTypeDAO;
import com.corticon.dialogs.dao.QuestionDAO;
import com.corticon.dialogs.dao.QuestionStatusTypeDAO;
import com.corticon.dialogs.dao.UserDAO;
import com.corticon.dialogs.dao.UserGroupDAO;
import com.corticon.dialogs.model.Question;
import com.corticon.dialogs.model.QuestionAvailabilityType;
import com.corticon.dialogs.model.QuestionStatusType;
import com.corticon.dialogs.model.User;
import com.corticon.dialogs.model.UserGroup;
import com.corticon.dialogs.util.Pager;

@Repository("questionDAO")
public class QuestionHibernateDAO extends GenericHibernateDAO<Question, Long> implements
        QuestionDAO {

    private Log logger = LogFactory.getLog(QuestionHibernateDAO.class);
    @Resource
    QuestionAvailabilityTypeDAO questionAvailabiliTytypeDAO;
    @Resource
    QuestionStatusTypeDAO questionStatusTypeDAO;
    @Resource
    UserDAO userDAO;
    @Resource
    UserGroupDAO userGroupDAO;

    @Override
    public long getCountByCriteria(String criteria, String availability, String status, String user,
                                   String group) {

        return this.getCount(this.constructCriteria(criteria, availability, status, user, group));
    }

    @Override
    public List<Question> getPageOfQuestions(String criteria, String availability, String status,
                                             String user, String group, Pager pager) {
        long pagesize = this.getCountByCriteria(criteria, availability, status, user, group);
        if (pagesize > 0) {
            pager.setPageSize(Integer.valueOf(String.valueOf(pagesize)));
        }
        return this.getPageOfData(this.constructCriteria("%" + criteria + "%", availability, status,
                user, group), pager);
    }

    /**
     * Construct the search criteria by user input
     *
     * @param criteria     The search keyword input by user
     * @param availability Search by availability
     * @param status       Search by status
     * @param user         Search by user
     * @param group        Search by group
     * @return criteria The final search criteria
     */
    private Criteria constructCriteria(String criteria, String availability, String status,
                                       String user, String group) {

        Criteria c = this.getBaseCriteria();

        String[] criterias = {criteria, availability, status, user, group}; //index sensible

        int countNull = 0;
        for (int i = 0; i < criterias.length; i++) {

            if (criterias[i] == null || criterias[i].equals("")) {
                criterias[i] = null;
                countNull++;
            }
        }

        if (countNull == 5) {//no cirteria was presented by user
            return c;
        }

        for (int i = 0; i < criterias.length; i++) {

            switch (i) {

                case 0:
                    if (criterias[0] != null && !criterias[0].equals("%%")) {
                        c.add(disjunction().add(ilike("refId", criterias[i])).add(ilike("text",
                                criterias[i])).
                                add(ilike("description", criterias[i], MatchMode.EXACT)));
                    }
                    break;

                case 1:
                    if (criterias[1] != null) {
                        QuestionAvailabilityType availabilityType = questionAvailabiliTytypeDAO.findById(criterias[1]);
                        c.add(eq("questionAvailabilityType", availabilityType));
                    }
                    break;

                case 2:
                    if (criterias[2] != null) {
                        QuestionStatusType questionStatus = questionStatusTypeDAO.findById(
                                criterias[2]);
                        c.add(eq("questionStatusType", questionStatus));
                    }
                    break;

                case 3:
                    if (criterias[3] != null) {
                        User qsUser = userDAO.findById(criterias[3]);
                        c.add(eq("modifiedBy", qsUser));
                    }
                    break;

                case 4:
                    if (criterias[4] != null) {
                        if (!criterias[4].equals("1=1")) {
                            UserGroup qsUserGroup = userGroupDAO.findById(criterias[4]);
                            logger.debug("==================group id is: " + qsUserGroup.getName());
                            // c.createCriteria("modifiedBy", "usr").add(eq("usr.userGroup", qsUserGroup));
                            c.createCriteria("modifiedBy", "usr").add(eq("usr.userGroup",
                                    qsUserGroup));
                            //c.add(eq("modifiedBy.userGroup", qsUserGroup));
                        }

                    }
                    break;
            }
        }
        return c;
    }

    public int GetMaxNumber() {
        List Lmax = this.findBySQLQuery("select max(a.id) from Q_Question a ");
        if (Lmax != null && Lmax.size() >= 1) {
            if (Lmax.get(0) == null) {
                return 0;
            }
            String CutPix = Lmax.get(0).toString().replace("Q", "");
            CutPix = CutPix.replace("-", "");
            return Integer.valueOf(CutPix);
        }
        return 0;
    }

    public Question GetQuestionById(String q_question_id) {
        return (Question) this.getSession().get(Question.class, q_question_id);
    }

    @Override
    public List<Question> searchUnincludedQuestions(String keywords, String username, String group) {
        Criteria criteria = getBaseCriteria();

        // criteria.add(ne("questionStatusType.code", BasicQuestionStatusType.retired.toString()));

        if (StringUtils.isNotBlank(keywords)) {
            criteria.add(disjunction().add(ilike("refId", keywords, MatchMode.ANYWHERE)).add(ilike("vocabularyname", keywords, MatchMode.ANYWHERE)).add(ilike("name", keywords,
                    MatchMode.ANYWHERE)).add(ilike("text", keywords, MatchMode.ANYWHERE)).add(ilike("description",
                    keywords, MatchMode.ANYWHERE)));
        }

        if (StringUtils.isNotEmpty(username)) {
            criteria.createCriteria("modifiedBy").add(eq("username", username));
        } else if (StringUtils.isNotEmpty(group)) {
            criteria.createCriteria("modifiedBy").createCriteria("userGroup").add(eq("name", group));
        }

        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        return criteria.list();
    }

    @Override
    public List<Question> search(String keywords, String availability, String status,
                                 String username, String groupName, Pager pager) {

        Criteria countCriteria = buildSearchCriteria(keywords, availability, status, username,
                groupName);
        pager.setNumOfRecords(getCount(countCriteria));

        Criteria criteria = buildSearchCriteria(keywords, availability, status, username, groupName);

        return getPageOfData(criteria, pager);
    }

    private Criteria buildSearchCriteria(String keywords, String availability, String status,
                                         String username, String groupName) {
        Criteria criteria = getBaseCriteria();
        keywords = (keywords == null ? "" : keywords.trim());

        if (StringUtils.isNotEmpty(keywords)) {
            criteria.createAlias("sibling", "s", CriteriaSpecification.LEFT_JOIN).add(disjunction().
                    add(ilike("refId", keywords, MatchMode.ANYWHERE)).add(ilike("name", keywords, MatchMode.ANYWHERE)).add(ilike("vocabularyname", keywords, MatchMode.ANYWHERE)).add(ilike("text", keywords,
                    MatchMode.ANYWHERE)).add(ilike("description", keywords, MatchMode.ANYWHERE)).add(ilike(
                    "s.refId", keywords,
                    MatchMode.ANYWHERE)));
        }

        if (StringUtils.isNotEmpty(availability)) {
            criteria.createCriteria("questionAvailabilityType").add(eq("code", availability));
        }

        if (StringUtils.isNotEmpty(status)) {
            criteria.createCriteria("questionStatusType").add(eq("code", status));
        }

        if (StringUtils.isNotEmpty(username)) {
            criteria.createCriteria("modifiedBy").add(eq("username", username));
        } else if (StringUtils.isNotEmpty(groupName)) {
            criteria.createCriteria("modifiedBy").createCriteria("userGroup").add(eq("name",
                    groupName));
        }

        return criteria;
    }

    @Override
    public String findMaxRefId() {
        return (String) getSession().getNamedQuery("Question.findMaxRefId").uniqueResult();
    }

    @Override
    public long getCountByGroup(UserGroup group) {
        return getCount(getCriteriaByGroup(group));
    }

    private Criteria getCriteriaByGroup(UserGroup group) {
        Criteria criteria = getBaseCriteria();
        criteria.createCriteria("modifiedBy").add(eq("userGroup", group));
        return criteria;
    }

    @Override
    public List<Question> getPageOfDataByGroup(UserGroup group, Pager pager) {
        return getPageOfData(getCriteriaByGroup(group), pager);
    }
}